﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;

namespace Logica
{
    public class Interprete
    {
        private const String pathCPP = "C:/Proyecto2/Salida.cpp";
        private const String tempCPP = "C:/Proyecto2/tmp.cpp";
        int m;
        int p;
        int[] pila;
        int[] mem;
        ArrayList temporales = new ArrayList();
        Nodo raiz;

        public String Ejecutar(String nombreClase, int tam, String nombre, ArrayList parametros)
        {
            ArrayList main = EscribirMain(nombreClase, tam, nombre, parametros);
            raiz = leerCPP(main);
            String resultado = EjecutarCod3Dir(raiz, nombre, parametros) ;
            return resultado;
        }

        private ArrayList EscribirMain(String nombreClasePublica, int tamanioClase, String nombreMetodo, ArrayList parametros)
        {
            ArrayList salida = new ArrayList();

            salida.Add("int main(){");
            salida.Add("//Creo el objeto");
            salida.Add("tm1 = p+0; //posicion del this");
            salida.Add("tm2 = m;   //Donde iniciara el objeto");
            salida.Add("m = m + " + tamanioClase + "; //Reservo la mmoria para el objeto");
            salida.Add("pila[tm1] = tm2; //Referencia al heap para el objeto");
            salida.Add("//Llamo al metodo");
            salida.Add("p = p + 1;");
            salida.Add("tm3 = p+ 0; //posicion del this del metodo al que se llama");
            salida.Add("pila[tm3] = tm2; //Valor del this");
            
            int cont = 4;
            int i = 2;
            String concat = "";
            foreach (Parametro param in parametros)
            {
                concat += "_" + param.stipo;
                int tipo = param.tipo;
                salida.Add("tm" + cont + "= p + " + i++ +";");
                switch (tipo)
                {
                    case Parametro.tipo_int:
                        salida.Add("pila[tm" + cont++ + "] = " + Int32.Parse(param.valor) +"; //le paso el parametro "+(i-1));  
                        break;
                    case Parametro.tipo_string:
                        break;
                    case Parametro.tipo_bool:
                        break;
                    case Parametro.tipo_char:
                        break;
                }
            }

            salida.Add(nombreClasePublica + "_" + nombreMetodo + concat + "();");
            salida.Add("tm" + cont++ + @"= p+1;");
            salida.Add("p = p-1;");
            salida.Add("tm"+ cont +"= pila[tm" + (cont-1) + "];");
            salida.Add("printf(\"%i\",tm"+cont+");");
            salida.Add("Lfin:t__0=0;");
            salida.Add("}");

            for (int ind = 1; ind <= cont; ind++)
            {
                temporales.Add(new Temporal("tm" + ind, 0));
            }
            return salida;            
        }

        private String EjecutarCod3Dir(Nodo raiz, String nombre, ArrayList parameros)
        {
            Nodo encabezado = raiz.getHijoEn(0);
            if (encabezado.getEtiqueta().Equals("encabezado"))
            {
                EjecutarEncabezado(encabezado);       
            }
            //Busco el main
            Nodo main = null;
            foreach (Nodo metodo in raiz.getHijoEn(1).getHijos())
            { 
                String nombre_metodo = metodo.getEtiqueta();
                if (nombre_metodo.Equals("main"))
                {
                    main = metodo;
                    break;
                }
            }
            return EjecutarMetodo(main);
        }

        private Temporal getTemporal(String nombre)
        {
            foreach (Temporal tmp in temporales)
            {
                if (tmp.nombre.Equals(nombre))
                {
                    return tmp;
                }
            }
            return null;
        }

        private String Evaluar(Nodo mNodo)
        {
            String etiq = mNodo.getEtiqueta();
            switch (etiq)
            {
                case "numero":
                    return mNodo.getHijoEn(0).getEtiqueta();
                case "puntero":
                    switch (mNodo.getHijoEn(0).getEtiqueta())
                    {
                        case "p":
                            return p.ToString();
                        case "m":
                            return m.ToString();
                    }
                    break;
                case "temporal":
                    return getTemporal(mNodo.getHijoEn(0).getEtiqueta()).valor + "";
                case "+":
                    return Int32.Parse(Evaluar(mNodo.getHijoEn(0))) + Int32.Parse(Evaluar(mNodo.getHijoEn(1))) + "";
                case "-":
                    return Int32.Parse(Evaluar(mNodo.getHijoEn(0))) - Int32.Parse(Evaluar(mNodo.getHijoEn(1))) + "";
                case "*":
                    return Int32.Parse(Evaluar(mNodo.getHijoEn(0))) * Int32.Parse(Evaluar(mNodo.getHijoEn(1))) + "";
                case "/":
                    return Int32.Parse(Evaluar(mNodo.getHijoEn(0))) / Int32.Parse(Evaluar(mNodo.getHijoEn(1))) + "";
                case "%":
                    return Int32.Parse(Evaluar(mNodo.getHijoEn(0))) % Int32.Parse(Evaluar(mNodo.getHijoEn(1))) + "";
                case "pila":
                    int pPos = Int32.Parse(Evaluar(mNodo.getHijoEn(0)));
                    return pila[pPos]+"";
                case "memoria":
                    String tPun = mNodo.getHijoEn(0).getEtiqueta();
                    switch (tPun)
                    {
                        case "puntero":
                            switch (mNodo.getHijoEn(0).getHijoEn(0).getEtiqueta())
                            {
                                case "p":
                                    return mem[p] + "";
                                case "m":
                                    return mem[m] + "";
                            }
                            break;
                        case "temporal":
                            return mem[getTemporal(mNodo.getHijoEn(0).getHijoEn(0).getEtiqueta()).valor] + "";
                    }
                    break;
                case "<=":
                    Boolean res1 = Int32.Parse(Evaluar(mNodo.getHijoEn(0))) <= Int32.Parse(Evaluar(mNodo.getHijoEn(1)));
                    if (res1 == true){
                        return "true";
                    }else{ 
                        return "false";
                    }
                case ">=":
                    Boolean res2 = Int32.Parse(Evaluar(mNodo.getHijoEn(0))) >= Int32.Parse(Evaluar(mNodo.getHijoEn(1)));
                    if (res2 == true){
                        return "true";
                    }else{ 
                        return "false";
                    }
                case "==":
                    Boolean res3 = Int32.Parse(Evaluar(mNodo.getHijoEn(0))) == Int32.Parse(Evaluar(mNodo.getHijoEn(1)));
                    if (res3 == true){
                        return "true";
                    }else{ 
                        return "false";
                    }
                case "!=":
                    Boolean res4 = Int32.Parse(Evaluar(mNodo.getHijoEn(0))) != Int32.Parse(Evaluar(mNodo.getHijoEn(1)));
                    if (res4 == true){
                        return "true";
                    }else{ 
                        return "false";
                    }
                default:
                    Console.Write("algo que no conozco");
                    break;
            }

            return "";
        }

        private String EjecutarMetodo(Nodo metodo)
        {
            int size = metodo.getHijos().Count;
            for (int i = 0; i < size; i++)
            {
                Nodo mNodo = (Nodo) metodo.getHijos()[i];
                String etiq = mNodo.getEtiqueta();
                switch (etiq)
                {
                    case "=":
                        String s = mNodo.getHijoEn(0).getEtiqueta();
                        switch (s)
                        {
                            case "temporal":
                                getTemporal(mNodo.getHijoEn(0).getHijoEn(0).getEtiqueta()).valor = Int32.Parse(Evaluar(mNodo.getHijoEn(1)));
                                break;
                            case "memoria":
                                String t = mNodo.getHijoEn(0).getHijoEn(0).getEtiqueta();
                                switch(t){
                                    case "temporal":
                                        int pos = getTemporal(mNodo.getHijoEn(0).getHijoEn(0).getHijoEn(0).getEtiqueta()).valor;
                                        mem[pos] = Int32.Parse(Evaluar(mNodo.getHijoEn(1)));
                                        break;
                                    case "puntero":
                                        String tipoP = mNodo.getHijoEn(0).getHijoEn(0).getHijoEn(0).getEtiqueta();
                                        switch (tipoP)
                                        {
                                            case "p":
                                                mem[p] = Int32.Parse(Evaluar(mNodo.getHijoEn(1)));
                                                break;
                                            case "m":
                                                mem[m] = Int32.Parse(Evaluar(mNodo.getHijoEn(1)));
                                                break;
                                        }
                                        break;
                                }
                                break;
                            case "pila":
                                int pos_pila = getTemporal(mNodo.getHijoEn(0).getHijoEn(0).getHijoEn(0).getEtiqueta()).valor;
                                pila[pos_pila] = Int32.Parse(Evaluar(mNodo.getHijoEn(1)));
                                break;
                            case "puntero":
                                switch (mNodo.getHijoEn(0).getHijoEn(0).getEtiqueta())
                                {
                                    case "p":
                                        p = Int32.Parse(Evaluar(mNodo.getHijoEn(1)));
                                        break;
                                    case "m":
                                        m = Int32.Parse(Evaluar(mNodo.getHijoEn(1)));
                                        break;
                                }
                                break;
                        }
                        break;
                    case "metodo":
                        Nodo nodo_metodo = null;
                        foreach (Nodo nMet in raiz.getHijoEn(1).getHijos())
                        {
                            String nombre_metodo = nMet.getEtiqueta();
                            if (nombre_metodo.Equals(mNodo.getHijoEn(0).getEtiqueta()))
                            {
                                nodo_metodo = nMet;
                                break;
                            }
                        }
                        EjecutarMetodo(nodo_metodo);
                        break;
                    case "etiqueta":
                        //No se hace nada porque el flujo de ejecucion es top-down
                        break;
                    case "if":
                        if (Evaluar(mNodo.getHijoEn(0)).Equals("true"))
                        {
                            int contIF = 0;
                            foreach (Nodo gt in metodo.getHijos())
                            {
                                if (gt.getEtiqueta().Equals("etiqueta"))
                                {
                                    if (gt.getHijoEn(0).getEtiqueta().Equals(mNodo.getHijoEn(1).getHijoEn(0).getEtiqueta()))
                                    {
                                        i = contIF;
                                        break;
                                    }
                                }
                                contIF++;
                            }
                        }
                        break;
                    case "goto":
                        int cont = 0;
                        foreach (Nodo gt in metodo.getHijos())
                        {
                            if (gt.getEtiqueta().Equals("etiqueta"))
                            {
                                if (gt.getHijoEn(0).getEtiqueta().Equals(mNodo.getHijoEn(0).getEtiqueta()))
                                {
                                    i = cont;
                                    break;
                                }
                            }
                            cont++;
                        }
                        break;

                    case "printf":

                        if(mNodo.getHijoEn(0).getEtiqueta().Equals("%c")) {
                         //entonces quiere imprimir un valor ascii:
                             return ((char)Int32.Parse(Evaluar(mNodo.getHijoEn(1))) + "");
                        }
                        else if(mNodo.getHijoEn(0).getEtiqueta().Equals("%i")){
                            //Entonces queres imprimir el valor integer
                            return (Int32.Parse(Evaluar(mNodo.getHijoEn(1))) +""); 
                        }
                        break;

                    case "printfenter":
                        Console.WriteLine();
                        break;

                    default:
                        Console.Write("algo que no conozco");
                        break;
                }
            }
            return "";
        }
        
        private void EjecutarEncabezado(Nodo encabezado)
        {
            foreach (Nodo n in encabezado.getHijos())
            {
                if (n.getEtiqueta().Equals("m"))
                {
                    int valor = Int32.Parse(n.getHijoEn(0).getEtiqueta());
                    m = valor;
                }
                else if (n.getEtiqueta().Equals("p"))
                {
                    int valor = Int32.Parse(n.getHijoEn(0).getEtiqueta());
                    p = valor;
                }
                else if (n.getEtiqueta().Equals("pila"))
                {
                    int valor = Int32.Parse(n.getHijoEn(0).getEtiqueta());
                    pila = new int[valor];
                }
                else if (n.getEtiqueta().Equals("mem"))
                {
                    int valor = Int32.Parse(n.getHijoEn(0).getEtiqueta());
                    mem = new int[valor];
                }
                else if(n.getEtiqueta().Equals("temporales"))
                {
                    foreach (Nodo temp in n.getHijos())
	                {
		                temporales.Add(new Temporal(temp.getEtiqueta(), 0));       
	                }
                }
            }
        }

        private Nodo leerCPP(ArrayList main)
        {
            interprete_scanner nvoScanner = null;
            interprete_parser nvoParser = null;

            try
            {
                File.Copy(pathCPP, tempCPP, true);

                using (StreamWriter sw = new StreamWriter(tempCPP, true))
                {
                    foreach (String ss in main)
                    {
                        sw.WriteLine(ss);
                    }
                }

                using (TextReader read = File.OpenText(tempCPP))
                {
                    nvoScanner = new interprete_scanner(read);
                    nvoParser = new interprete_parser(nvoScanner);

                    nvoParser.parse();

                    Nodo n = parser.raiz;

                    Console.WriteLine("Se ejecuto con exito");

                }
                File.Delete(tempCPP);
                return parser.raiz;

            }
            catch (Exception ex)
            {
                Console.WriteLine("Hubo un error");
            }
            return null;
        }
    }
}
